package Reflect;

import Reflect.objectReflect.ReflectPoint;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Description :
 * @author: cww
 * @DateTime: 2018-07-29 21:22
 * 反射实现的过程
 */

public class ReflectStatic2 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        /**
         * 用反射实现一个new String(new StringBuffer("abc"))这个对象的产生
         */
        System.out.println("=====用反射实现一个new String(new StringBuffer(\"abc\"))这个对象的产生====");
        //1.通过String.class字节码拿到String的构造函数，该构造函数的参数类型为StringBuffer,表示选String多个构造函数的指定一个
         Constructor constructor = String.class.getConstructor(StringBuffer.class);
         //2.通过构造函数获得对象的实例，此时传入的是对象的实例,编译的时候只是编译成0101的class文件，虚拟机在运行时知道是构造方法，
        // 但并不知道构造方法是哪个对象的，所以必须指定构造方法的参数
         String string = (String) constructor.newInstance(new StringBuffer("abc"));
        System.out.println(string.charAt(1));


        /**
         * 不带参数的new String()
         * 如果获取不带参数的构造方法，那么在创建实例时也不能带参否则会报找不到参数的错
         */
        System.out.println("=====通过反射新建一个new String()对象，并打印对象名称=====");
        Constructor constructor1 = String.class.getConstructor();
        String s1 = (String) constructor1.newInstance();
        System.out.println("对象名称为 "+s1.getClass().getName());


        /**
         * objectReflect
         */
        ReflectPoint pt1 = new ReflectPoint(3,5);
        //注意：getField()只能获取到可见的变量也就是public对象，private拿不到
        Field fieldY = pt1.getClass().getField("y");
        //field 不是对象身上的变量，而是类上的变量，要具体类中的某个值则要指定具体是哪个对象身上取的
        System.out.println("=====通过反射获取ReflectPoint中的变量y的值=====");
        System.out.println(fieldY.get(pt1));

        /**
         * 暴力反射
         */
        //要访问私有变量，用getDeclaredField，但是获取到但还不能执行
        Field fieldX = pt1.getClass().getDeclaredField("x");
        //设置不可访问的为可访问的
        fieldX.setAccessible(true);
        System.out.println(fieldX.get(pt1));

        /**
         * 用反射替换对象中String类型变量的e为o
         */
        System.out.println("====用反射替换对象中的变量值====");
        replaceStringValueByReflect(pt1);
        System.out.println(pt1);


        /**
         * 用反射得到类中的某个方法
         * 调用str1中的charAt(1)方法
         */
        System.out.println("=====用反射得到类中的某个方法====");
        String str1 = "qwe";
        //通过字节码获取名字为"charAt"的方法，因为一个方法有多种重载形式，要用哪种重载方式要指定参数类型，charAt只有一种
        Method method = str1.getClass().getMethod("charAt",int.class);
        //目标类参数为str1，调用方法的时候需要的参数为1，对应上面的参数类型，因为参数类型只有一个，则这里的参数也是只有一个；
        // 实际上是动态代理，invoke执行的是方法对象(Method)里面的方法
        System.out.println(method.invoke(str1,1));
    }

    private static void replaceStringValueByReflect(Object o) throws IllegalAccessException {
        //1.获取成员变量s
        Field[] fields = o.getClass().getFields();
        //逐一找出每个变量的值
        for (Field field:fields){
            //判断该变量是否是String类型,判断字节码是否相等用==，因为内存中只存在一个
            if (field.getType() == String.class){
                String oldStr = (String) field.get(o);
                String newStr = oldStr.replace("e","o");
                //将替换后的对象设值回去
                field.set(o,newStr);
            }
        }
    }
}
